﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace GameEngine.Camera
{
    public class Camera2D
    {
        protected float _zoom; // Camera Zoom
        public Matrix _transform; // Matrix Transform
        public Vector2 _pos; // Camera Position
        protected float _rotation; // Camera Rotation
        public Rectangle? _limits;

        private readonly GraphicsDevice _device;
        private Game game;

        public Camera2D(Game game, GraphicsDevice device)
        {
            _zoom = 1.0f;
            _rotation = 0.0f;
            _pos = Vector2.Zero;
            _device = device;
            this.game = game;
        }

        /// <summary>
        /// Gets or sets the zoom.
        /// </summary>
        /// <value>
        /// The zoom.
        /// </value>
        public float Zoom
        {
            get { return _zoom; }
            set {
                _zoom = value; if (_zoom < 0.1f) _zoom = 0.1f;
                ValidateZoom();
                ValidatePosition();
            } // Negative zoom will flip image
        }

        /// <summary>
        /// Gets or sets the rotation.
        /// </summary>
        /// <value>
        /// The rotation.
        /// </value>
        public float Rotation
        {
            get { return _rotation; }
            set { _rotation = value; }
        }

        // Auxiliary function to move the camera
        public void Move(Vector2 amount)
        {
            _pos += amount;
        }

        /// <summary>
        /// Gets or sets the pos.
        /// </summary>
        /// <value>
        /// The pos.
        /// </value>
        public Vector2 Pos
        {
            get { return _pos; }
            set { 
                _pos = value;
                ValidatePosition();
            }
        }

        /// <summary>
        /// Sets the limits.
        /// </summary>
        /// <value>
        /// The limits.
        /// </value>
        public Rectangle? Limits
        {
            set
            {
                _limits = value;
                ValidateZoom();
                ValidatePosition();
            }
        }

        /// <summary>
        /// Validates the position.
        /// </summary>
        private void ValidatePosition()
        {
            if (_limits.HasValue)
            {
                Vector2 cameraWorldMin = Vector2.Transform(Vector2.Zero, Matrix.Invert(GetTransformation()));
                Vector2 cameraSize = new Vector2(_device.Viewport.Width, _device.Viewport.Height) / _zoom;
                Vector2 limitWorldMin = new Vector2(_limits.Value.Left, _limits.Value.Top);
                Vector2 limitWorldMax = new Vector2(_limits.Value.Right, _limits.Value.Bottom);
                Vector2 positionOffset = _pos - cameraWorldMin;
                _pos = Vector2.Clamp(cameraWorldMin, limitWorldMin, limitWorldMax - cameraSize) + positionOffset;
            }
        }

        /// <summary>
        /// Validates the zoom.
        /// </summary>
        private void ValidateZoom()
        {
            if (_limits.HasValue)
            {
                float minZoomX = (float)_device.Viewport.Width / _limits.Value.Width;
                float minZoomY = (float)_device.Viewport.Height / _limits.Value.Height;
                _zoom = MathHelper.Max(_zoom, MathHelper.Max(minZoomX, minZoomY));
            }
        }

        /// <summary>
        /// Gets the transformation.
        /// </summary>
        /// <returns></returns>
        public Matrix GetTransformation()
        {
            return Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
                   Matrix.CreateRotationZ(Rotation) *
                   Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *
                   Matrix.CreateTranslation(new Vector3(_device.Viewport.Width * 0.5f, _device.Viewport.Height * 0.5f, 0));
        }
    }
}
